package com.leehom.arch.base.reportquery.jpa;


import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.hibernate.query.NativeQuery;
import org.hibernate.transform.Transformers;
import org.hibernate.type.StandardBasicTypes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ResourceUtils;

import com.leehom.arch.base.reportquery.ReportQuery;
import com.leehom.arch.base.reportquery.dynsql.DynSql;
import com.leehom.arch.base.reportquery.utils.SortParamMerger;
import com.leehom.arch.base.serializer.Serializer;
import com.leehom.arch.base.serializer.SeriallizeException;
import com.leehom.arch.base.utils.ByteAndStreamUtils;
import com.leehom.arch.common.MessageCode;
import com.leehom.arch.common.Page;
import com.leehom.arch.common.SortBean;
import com.leehom.arch.common.SortOrder;

import jakarta.annotation.PostConstruct;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.persistence.Query;

/**
 * @author leehom
 * @类名: ReportQueryJpaImpl
 * @说明: 报表动态查询jpa实现
 * @Date Sep 11, 2019 7:38:47 PM
 * 修改记录：
 * @see
 */
@Service
public class ReportQueryJpaImpl implements ReportQuery {

    private Logger log = LoggerFactory.getLogger(this.getClass());
    /**
     *
     */
    public static final String TOTAL_FIELD_NAME = "total";
    /**
     * 总数查询sql后缀
     */
    public static final String TOTAL_PAGE_POSTFIX = "_Total";

    @PersistenceContext
    private EntityManager em;
    /**
     *
     */
    @Autowired
    private Serializer dynsqlSer;
    /**
     * 模板集合
     */
    private Map<String, DynSql> dynSqls;

    // 加载动态sql模版
    @SuppressWarnings("unchecked")
    @PostConstruct
    protected void init() {
        try {
            Resource[] resources = new PathMatchingResourcePatternResolver().
                    getResources(ResourceUtils.CLASSPATH_URL_PREFIX + "dynsql/*.xml");
            //
            dynSqls = new HashMap<String, DynSql>();
            for (Resource f : resources) {
                InputStream is = f.getInputStream();
                byte[] bs = ByteAndStreamUtils.StreamToBytes(is);
                //
                Map<String, DynSql> t = (Map<String, DynSql>) dynsqlSer.Unmarshal(bs);
                dynSqls.putAll(t);
            }
            // 初始化动态sql
            for (DynSql value : dynSqls.values()) {
                value.init();
            }
        } catch (SeriallizeException | IOException e) {
            throw new RuntimeException(MessageCode.REPORTQUERY_LOAD_SQL_TEMPLATE_FAIL.getCodeStr());
        }

    }

    @Override
    public DynSql dynSql(String name) {
        return dynSqls.get(name);
    }

    @Transactional
    @Override
    public int execSql(String sqlName, Map<String, Object> params) {
        // 获取命名sql模版
        final DynSql dynSql = dynSqls.get(sqlName);
        // 构建sql
        final String sql = dynSql.buildSQL(params);
        // 构建query
        Query query = em.createNativeQuery(sql);
        //
        this.paramedQuery(query, params);
        //
        return query.executeUpdate();

    }

    @Transactional
    @Override
    public int execJpql(String jpqlName, Map<String, Object> params) {
        // 获取命名sql模版
        final DynSql dynSql = dynSqls.get(jpqlName);
        // 构建sql
        final String sql = dynSql.buildSQL(params);
        // 构建query
        Query query = em.createQuery(sql);
        //
        this.paramedQuery(query, params);
        //
        return query.executeUpdate();
    }

    @Override
    public Object queryNamedSQL(String sqlName, Map<String, Object> params) {
        // 获取命名sql模版
        final DynSql dynSql = dynSqls.get(sqlName);
        //
        return this.queryNamedSQL(dynSql, params);
    }

    @SuppressWarnings("deprecation")
    @Override
    public Object queryNamedSQL(DynSql dynSql, Map<String, Object> params) {
        // 构建sql
        final String sql = dynSql.buildSQL(params);
        // 构建query
        Query query = em.createNativeQuery(sql);
        // 结果集转换为map
        query.unwrap(NativeQuery.class).setResultTransformer(Transformers.aliasToBean(dynSql.getResultClazz()));
        //
        this.paramedQuery(query, params);
        // 获取结果
        return query.getResultList();
    }

    @Override
    public Object queryNamedSQL(String sqlName, Map<String, Object> params, String resultKey) {
        // 获取命名sql模版
        final DynSql dynSql = dynSqls.get(sqlName);
        // 构建sql
        final String sql = dynSql.buildSQL(params);
        // 构建query
        Query query = em.createNativeQuery(sql, resultKey);
        //
        this.paramedQuery(query, params);
        // 获取结果
        return query.getResultList();
    }

    @Override
    public Object queryNamedSQL(DynSql dynSql, Map<String, Object> params, String resultKey) {
        // 构建sql
        final String sql = dynSql.buildSQL(params);
        // 构建query
        Query query = em.createNativeQuery(sql, resultKey);
        //
        this.paramedQuery(query, params);
        // 获取结果
        return query.getResultList();
    }
	
	// 分页处理
	public Page<Object> doPageSql(Page<Object> page, String sqlName, List result, Map<String, Object> params) {
		//
		Page<Object> p = new Page<Object>();
		p.setRows(result);
		p.setPage(page.getPage());
		p.setPageSize(page.getPageSize());
		// 计算总页数
		if (page.getTotal() == null) {
			String totalSql = sqlName + TOTAL_PAGE_POSTFIX;
			;
			long t = this.totalQuerySql(totalSql, params);
			p.setTotal(t);
		}
		return p;
	}
	
	// JPQL总数查询
	public Long totalQueryJpql(String jpqlName, Map<String, Object> params) {
		// 获取命名sql模版
		final DynSql template = dynSqls.get(jpqlName);
		// 构建sql
		final String sql = template.buildSQL(params);
		// 构建query
		Query query = em.createQuery(sql);
		// 参数
		this.paramedQuery(query, params);
		// 获取结果
	    Long result = (Long)query.getSingleResult();
	    //
		return result;
	}
	
	// SQL总数查询
	public Long totalQuerySql(String sqlName, Map<String, Object> params) {
		//
		// 获取命名sql模版
		final DynSql template = dynSqls.get(sqlName);
		// 构建sql
		final String sql = template.buildSQL(params);
		// 构建query
		Query query = em.createNativeQuery(sql);
		// 参数
		this.paramedQuery(query, params);
		//
		query.unwrap(NativeQuery.class).addScalar(TOTAL_FIELD_NAME, StandardBasicTypes.LONG);
		// 获取结果
	    Long result = (Long)query.getSingleResult();
	    //
		return result;
	}
	
	// 分页	
	private Query pagedQuery(Query query, Page<?> page) {
		query.setFirstResult((page.getPage() - 1) * page.getPageSize())
				.setMaxResults(page.getPageSize());
		return query;
	}
	
	// 参数
	private Query paramedQuery(Query query, Map<String, Object> params) {
		for(Map.Entry<String, Object> entry : params.entrySet()){
		    String key = entry.getKey();
		    Object value = entry.getValue();
		    // 排序参数/布尔忽略
		    if(value instanceof SortOrder)
		    	continue;
		    //
		    query.setParameter(key, value);
		}
		return query;
	}
	
    @Override
    public Object queryNamedHQL(DynSql dynSql, Map<String, Object> params) {
        // 构建sql
        final String sql = dynSql.buildSQL(params);
        // 构建query
        Query query = em.createQuery(sql);
        // 设置参数
        this.paramedQuery(query, params);
        // 获取结果
        return query.getResultList();
    }

    @Override
    public Page pageQueryNamedSQL(Page page, String sqlName, DynSql dynSql, Map<String, Object> params) {
        // 构建sql
        final String sql = dynSql.buildSQL(params);
        // 构建query
        Query query = em.createNativeQuery(sql);
        // 结果集转换为map
        query.unwrap(NativeQuery.class).setResultTransformer(Transformers.aliasToBean(dynSql.getResultClazz()));
        // 设置分页&参数
        this.pagedQuery(query, page);
        this.paramedQuery(query, params);
        // 获取结果
        List result = query.getResultList();
        //
        return this.doPageSql(page, sqlName, result, params);
    }

    @Override
    public Page pageQueryNamedHQL(Page page, DynSql dynSql, Map<String, Object> params) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Object queryNamedHQL(String jpqlName, Map<String, Object> params) {
        // 获取命名sql模版
        final DynSql dynSql = dynSqls.get(jpqlName);
        //
        return this.queryNamedHQL(dynSql, params);

    }

    @Override
    public Page pageQueryNamedSQL(Page page, String sqlName, Map<String, Object> params) {
        // 获取命名sql模版
        final DynSql dynSql = dynSqls.get(sqlName);
        //
        return pageQueryNamedSQL(page, sqlName, dynSql, params);
    }

    @Override
    public Page pageQueryNamedHQL(Page page, String jpqlName, Map<String, Object> params) {
        //
        return this.doPageQueryJpql(page, jpqlName, params);
    }

    // 执行sql分页查询
    @SuppressWarnings("deprecation")
    public Page doPageQuerySql(Page page, String sqlName, Map<String, Object> params) {
        // 获取命名sql模版
        final DynSql dynSql = dynSqls.get(sqlName);
        // 构建sql
        final String sql = dynSql.buildSQL(params);
        // 构建query
        Query query = em.createNativeQuery(sql);
        // 结果集转换为map
        query.unwrap(NativeQuery.class).setResultTransformer(Transformers.aliasToBean(dynSql.getResultClazz()));
        // 设置分页&参数
        this.pagedQuery(query, page);
        this.paramedQuery(query, params);
        // 获取结果
        List result = query.getResultList();
        //
        return this.doPageSql(page, sqlName, result, params);

    }

    // 执行jpql分页查询
    @SuppressWarnings("unchecked")
    public Page doPageQueryJpql(Page page, String jpqlName, Map<String, Object> params) {
        // 获取命名sql模版
        final DynSql dynSql = dynSqls.get(jpqlName);
        // 构建sql
        final String sql = dynSql.buildSQL(params);
        // 构建query
        Query query = em.createQuery(sql, dynSql.getResultClazz());
        // 设置分页&参数
        this.pagedQuery(query, page);
        this.paramedQuery(query, params);
        // 获取结果
        List result = query.getResultList();
        //
        return this.doPageJpql(page, jpqlName, result, params);

    }

    // 分页处理
    public Page doPageJpql(Page page, String jpqlName, List result, Map<String, Object> params) {
        //
        Page<Object> p = new Page<Object>();
        p.setRows(result);
        p.setPage(page.getPage());
        p.setPageSize(page.getPageSize());
        // 计算总页数
        if (page.getTotal() == null) {
            String totalSql = jpqlName + TOTAL_PAGE_POSTFIX;
            ;
            long t = this.totalQueryJpql(totalSql, params);
            p.setTotal(t);
        }
        return p;
    }
    
    public Page PageQueryJpql(Page page, String jpqlName, Map<String, Object> params, SortBean sortBean) {
        // 获取命名sql模版
        final DynSql dynSql = dynSqls.get(jpqlName);
        if(sortBean!=null){
            SortParamMerger.merge(params, sortBean);
        }
        // 构建sql
        final String sql = dynSql.buildSQL(params);
        // 构建query
        Query query = em.createQuery(sql, dynSql.getResultClazz());

        // 设置分页&参数
        this.pagedQuery(query, page);
        this.paramedQuery(query, params);
        // 获取结果
        List result = query.getResultList();
        //
        return this.TotalSql(page, jpqlName, result, params);

    }

    // 分页处理
    public Page TotalSql(Page page, String sqlName, List result, Map<String, Object> params) {
        //
        Page<Object> p = new Page<Object>();
        p.setRows(result);
        p.setPage(page.getPage());
        p.setPageSize(page.getPageSize());
        // 计算总页数
        if (page.getTotal() == null) {
            String totalSql = sqlName + TOTAL_PAGE_POSTFIX;
            ;
            long t = this.totalQuerySql(totalSql, params);
            p.setTotal(t);
        }
        return p;
    }
}

